//
//  DataStore.h
//
//  Created by Jeremy Whitcher on 12/22/13.
//

#import "DataStore.h"
#import "Constants.h"

#define DATASTORE_VERSION     "1.1"

#define DATASTORE_FILENAME     "AppData"
#define DATASTORE_DATA_FILEEXT "dat"
#define DATASTORE_VERSION_KEY  "DataStoreVersion"
#define DEFAULT_SETTINGS_KEY   "DefaultSettings"
#define ITEM_RECORD_KEY        "ItemRecord"

@implementation DataStore

static DataStore *gInstance = NULL;

@synthesize dataStoreVersion;
@synthesize defaultSettings;
@synthesize itemRecords;
@synthesize exportRecord;
@synthesize editRecord;          // Reference to current record being used
@synthesize index;               // Index of current record being used
@synthesize action;              // Type of action being performed.

+ (DataStore *)instance
{
    @synchronized(self)
    {
        if (gInstance == NULL)
        {
            gInstance = [[self alloc] init];
            
            if ([DataStore restore] == NO)
            {
                [DataStore setDefaultData];
            }
        }
    }
    
    return(gInstance);
}

+ (ItemRecord *) restoreRecord : (NSString *) fileName
{
    NSData *dataArea;
    NSKeyedUnarchiver *unarchiver;
    
    // Create a Path to the data record file
    NSString *path = [NSString stringWithString:NSHomeDirectory()];
    path = [path stringByAppendingPathComponent:@"Documents"];
    path = [path stringByAppendingPathComponent:fileName];
    
    // Open the data file and create an unarchiver
    dataArea = [NSData dataWithContentsOfFile:path];
    if (!dataArea) {
        NSLog (@"DataStore restoreRecord failed, could not open file.");
        return nil;
    }
    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData: dataArea];

    ItemRecord *decodedRecord = [unarchiver decodeObjectForKey:@ITEM_RECORD_KEY];
   
    return decodedRecord;
}

+ (NSArray *) GetDirectory : (NSString* ) path : (NSString *)extension
{
    NSFileManager *fmgr = [NSFileManager defaultManager];
    
    NSArray *dirContents = [fmgr contentsOfDirectoryAtPath:path error:nil];
    
    NSString *predicateFmt = [NSString stringWithFormat : @"self ENDSWITH '%@'", extension];
    
    NSPredicate *fltr = [NSPredicate predicateWithFormat : predicateFmt];
    
    NSArray *dirWithExtContents = [dirContents filteredArrayUsingPredicate:fltr];
    
    return dirWithExtContents;
}

+ (BOOL)restore
{
    if (gInstance != NULL)
    {
        DataStore *ds = gInstance;

        NSData *dataArea;
        NSKeyedUnarchiver *unarchiver;

        // Create a Path to the data file
        NSString *path = [NSString stringWithString:NSHomeDirectory()];
        path = [path stringByAppendingPathComponent:@"Documents"];
        path = [path stringByAppendingPathComponent:@DATASTORE_FILENAME];
        
        // Open the data file and create an unarchiver
        dataArea = [NSData dataWithContentsOfFile:path];
        if (!dataArea) {
            NSLog (@"DataStore restore failed, could not open file.");
            return NO;
        }
        unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData: dataArea];

        // Decode the objects previously stored in the archive
        [ds setDataStoreVersion:[unarchiver decodeObjectForKey:@DATASTORE_VERSION_KEY]];
        
        // Check the stored version number
        if (!([[ds dataStoreVersion] isEqualToString:@DATASTORE_VERSION])) {
            NSLog (@"DataStore restore failed, wrong version of data store file found.");
            return NO;
        }
        
        [ds setDefaultSettings:[unarchiver decodeObjectForKey:@DEFAULT_SETTINGS_KEY]];
                
        // reset the export record
        [ds setExportRecord:Nil];
        
        // reset the edit record
        [ds setEditRecord:Nil];
        
        // read each of the item records from their individual files
        ItemRecords *itemRecords = [[ItemRecords alloc] init];
        
        // get a directory of files
        path = [NSString stringWithString:NSHomeDirectory()];
        path = [path stringByAppendingPathComponent:@"Documents"];

        NSArray *dirContents = [NSArray arrayWithArray : [self GetDirectory : path : @DATASTORE_FILEEXT]];
        
        for (NSString * filePath in dirContents)
        {
            [itemRecords addRecord : [self restoreRecord : filePath]];
        }
        
        [ds setItemRecords : itemRecords];
        
        return YES;
    }
    return NO;
}

+ (NSString *)createUniqueFileName : (NSString*)prefixString : (NSString*) extension
{
    // create a unique ID object
    CFUUIDRef uuid = CFUUIDCreate(NULL);
    CFStringRef uuidString = CFUUIDCreateString(NULL, uuid);
    CFRelease(uuid);
    
    // create the filename string
    NSString *uniqueFileName = [NSString stringWithFormat:@"%@_%@", prefixString, (__bridge_transfer NSString *)uuidString];
        
    // add the file extension
    uniqueFileName = [uniqueFileName stringByAppendingPathExtension : extension];
    
    return uniqueFileName;
}

+ (void)deleteFile : (NSString *)fileName
{
    // Create file manager
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSError *error;

    // Create a Path to the file
    NSString *path = [NSString stringWithString : NSHomeDirectory()];
    path = [path stringByAppendingPathComponent : @"Documents"];
    path = [path stringByAppendingPathComponent : fileName];
    
    // Attempt to delete the file at Path
    if ([fileMgr removeItemAtPath:path error : &error] != YES)
        NSLog(@"DataStore deleteFile Unable to delete file: %@", [error localizedDescription]);
}

+ (void)deleteAllFiles : (NSString *)extension
{
    // get a directory of files
    NSString *path = [NSString stringWithString : NSHomeDirectory()];
    path = [NSString stringWithString:NSHomeDirectory()];
    path = [path stringByAppendingPathComponent:@"Documents"];
    
    NSArray *dirContents = [NSArray arrayWithArray : [self GetDirectory : path : extension]];
    
    for (NSString * filePath in dirContents)
    {
        [self deleteFile : filePath];
    }
}

+ (void)storeRecord : (ItemRecord *)rec
{
    NSMutableData *dataArea;
    NSKeyedArchiver *archiver;

    // If a record for the file already exists, delete it
    if ([[rec fileName] length] > 0)
    {
        [self deleteFile : [rec fileName]];
    }

    // If the item was deleted don't store a replacement file
    if ([rec itemDeleted])
        return;
    
    // Create a new unique path for the record file
    NSString *uniqueFileName = [NSString stringWithString:[self createUniqueFileName : @DATASTORE_FILENAME : @DATASTORE_FILEEXT]];
    NSString *path = [NSString stringWithString : NSHomeDirectory()];
    path = [path stringByAppendingPathComponent : @"Documents"];
    path = [path stringByAppendingPathComponent : uniqueFileName];

    // Store the path so on the next save the file can be deleted
    [rec setFileName : uniqueFileName];
    
    // Set up a data area and connect it to an NSKeyedArchiver object
    dataArea = [NSMutableData data];
    archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: dataArea];

    // Archive Record
    [archiver encodeObject: rec forKey: @ITEM_RECORD_KEY];

    // Finish archiving objects
    [archiver finishEncoding];
            
    // Write the archived record area to a file
    if ([dataArea writeToFile: path atomically: YES] == NO)
        NSLog (@"DataStore storeRecord failed");
}

+ (void)store
{
    if (gInstance != NULL)
    {
        DataStore *ds = gInstance;
        
        NSMutableData *dataArea;
        NSKeyedArchiver *archiver;
        
        // Set up a data area and connect it to an NSKeyedArchiver object
        dataArea = [NSMutableData data];
        archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData: dataArea];
        
        // Begin to archive objects

        // Data Store Version
        [archiver encodeObject: [ds dataStoreVersion] forKey: @DATASTORE_VERSION_KEY];
        
        // Default Settings
        [archiver encodeObject: [ds defaultSettings] forKey: @DEFAULT_SETTINGS_KEY];

        // Finish archiving objects
        [archiver finishEncoding];
        
        // Create a Path to the data file
        NSString *path = [NSString stringWithString : NSHomeDirectory()];
        path = [path stringByAppendingPathComponent : @"Documents"];
        path = [path stringByAppendingPathComponent : @DATASTORE_DATA_FILENAME];
        
        // Write the archived data area to a file
        if ([dataArea writeToFile: path atomically: YES] == NO)
            NSLog (@"DataStore store failed");
        
        // Write the records to file
        int count = [[ds itemRecords] getCount];
        for (int recIndex = 0; recIndex < count; recIndex++)
        {
            ItemRecord *rec = [[ds itemRecords] getRecord:recIndex];
            if (rec)
            {
                if ([rec itemModified])
                {
                    [rec setItemModified : FALSE];
                    [self storeRecord:rec];
                }
            }
        }

    }
}

+ (void)setDefaultData
{
    if (gInstance != NULL)
    {
        NSLog (@"DataStore setting default data.");
        
        DataStore *ds = gInstance;
                 
        NSString *dsVersion = [[NSString alloc] initWithString:@DATASTORE_VERSION];
        NSString *location  = [[NSString alloc] initWithString:DefaultLocation];
        NSString *contact   = [[NSString alloc] initWithString:DefaultContact];
        NSString *email     = [[NSString alloc] initWithString:DefaultEmail];
        NSString *phone     = [[NSString alloc] initWithString:DefaultPhone];        
        
        // Create a default settings object
        DefaultSettings *defaultSettings = [[DefaultSettings alloc] init];

        [defaultSettings setDefaultLocation:location];
        [defaultSettings setDefaultContact:contact];
        [defaultSettings setDefaultEmail:email];
        [defaultSettings setDefaultPhone:phone];
                
        // Create a default item record array
        ItemRecords *itemRecords = [[ItemRecords alloc] init];
                         
        // Assign the default data
        [ds setDataStoreVersion:dsVersion];
        [ds setDefaultSettings:defaultSettings];
        [ds setItemRecords:itemRecords];
        
        [self deleteAllFiles : @DATASTORE_DATA_FILEEXT];
        
        // Store the default record file
        [DataStore store];
    }
}

@end
